/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkTime.h"
#include "SkInterpolator.h"

extern bool is_overview(SkView* view);

static const char gIsTransitionQuery[] = "is-transition";
static const char gReplaceTransitionEvt[] = "replace-transition-view";

bool is_transition(SkView* view)
    {
    SkEvent isTransition(gIsTransitionQuery);
    return view->doQuery(&isTransition);
    }

class TransitionView : public SampleView
    {
    public:
        TransitionView(SkView* prev, SkView* next, int direction) : fInterp(4, 2)
            {
            fAnimationDirection = (Direction)(1 << (direction % 8));

            fPrev = prev;
            fPrev->setClipToBounds(false);
            fPrev->setVisibleP(true);
            (void)SampleView::SetUsePipe(fPrev, false);
            //Not calling unref because fPrev is assumed to have been created, so
            //this will result in a transfer of ownership
            this->attachChildToBack(fPrev);

            fNext = next;
            fNext->setClipToBounds(true);
            fNext->setVisibleP(true);
            (void)SampleView::SetUsePipe(fNext, false);
            //Calling unref because next is a newly created view and TransitionView
            //is now the sole owner of fNext
            this->attachChildToFront(fNext)->unref();

            fDone = false;
            //SkDebugf("--created transition\n");
            }

        ~TransitionView()
            {
            //SkDebugf("--deleted transition\n");
            }

        virtual void requestMenu(SkOSMenu* menu)
            {
            if (SampleView::IsSampleView(fNext))
                ((SampleView*)fNext)->requestMenu(menu);
            }

    protected:
        virtual bool onQuery(SkEvent* evt)
            {
            if (SampleCode::TitleQ(*evt))
                {
                SkString title;
                if (SampleCode::RequestTitle(fNext, &title))
                    {
                    SampleCode::TitleR(evt, title.c_str());
                    return true;
                    }
                return false;
                }
            if (evt->isType(gIsTransitionQuery))
                {
                return true;
                }
            return this->INHERITED::onQuery(evt);
            }
        virtual bool onEvent(const SkEvent& evt)
            {
            if (evt.isType(gReplaceTransitionEvt))
                {
                fPrev->detachFromParent();
                fPrev = (SkView*)SkEventSink::FindSink(evt.getFast32());
                (void)SampleView::SetUsePipe(fPrev, false);
                //attach the new fPrev and call unref to balance the ref in onDraw
                this->attachChildToBack(fPrev)->unref();
                this->inval(NULL);
                return true;
                }
            if (evt.isType("transition-done"))
                {
                fNext->setLoc(0, 0);
                fNext->setClipToBounds(false);
                SkEvent* evt = new SkEvent(gReplaceTransitionEvt,
                                           this->getParent()->getSinkID());
                evt->setFast32(fNext->getSinkID());
                //increate ref count of fNext so it survives detachAllChildren
                fNext->ref();
                this->detachAllChildren();
                evt->post();
                return true;
                }
            return this->INHERITED::onEvent(evt);
            }
        virtual void onDrawBackground(SkCanvas* canvas) {}
        virtual void onDrawContent(SkCanvas* canvas)
            {
            if (fDone)
                return;

            if (is_overview(fNext) || is_overview(fPrev))
                {
                fUsePipe = false;
                }

            SkScalar values[4];
            SkInterpolator::Result result = fInterp.timeToValues(SkTime::GetMSecs(), values);
            //SkDebugf("transition %x %d pipe:%d\n", this, result, fUsePipe);
            //SkDebugf("%f %f %f %f %d\n", values[0], values[1], values[2], values[3], result);
            if (SkInterpolator::kNormal_Result == result)
                {
                fPrev->setLocX(values[kPrevX]);
                fPrev->setLocY(values[kPrevY]);
                fNext->setLocX(values[kNextX]);
                fNext->setLocY(values[kNextY]);
                this->inval(NULL);
                }
            else
                {
                (new SkEvent("transition-done", this->getSinkID()))->post();
                fDone = true;
                }
            }

        virtual void onSizeChange()
            {
            this->INHERITED::onSizeChange();

            fNext->setSize(this->width(), this->height());
            fPrev->setSize(this->width(), this->height());

            SkScalar lr = 0, ud = 0;
            if (fAnimationDirection & (kLeftDirection|kULDirection|kDLDirection))
                lr = this->width();
            if (fAnimationDirection & (kRightDirection|kURDirection|kDRDirection))
                lr = -this->width();
            if (fAnimationDirection & (kUpDirection|kULDirection|kURDirection))
                ud = this->height();
            if (fAnimationDirection & (kDownDirection|kDLDirection|kDRDirection))
                ud = -this->height();

            fBegin[kPrevX] = fBegin[kPrevY] = 0;
            fBegin[kNextX] = lr;
            fBegin[kNextY] = ud;
            fNext->setLocX(lr);
            fNext->setLocY(ud);

            if (is_transition(fPrev))
                lr = ud = 0;
            fEnd[kPrevX] = -lr;
            fEnd[kPrevY] = -ud;
            fEnd[kNextX] = fEnd[kNextY] = 0;
            SkScalar blend[] = {0.8, 0.0, 0.0, 1.0};
            fInterp.setKeyFrame(0, SkTime::GetMSecs(), fBegin, blend);
            fInterp.setKeyFrame(1, SkTime::GetMSecs()+500, fEnd, blend);
            }

    private:
        enum
            {
            kPrevX = 0,
            kPrevY = 1,
            kNextX = 2,
            kNextY = 3
            };
        SkView* fPrev;
        SkView* fNext;
        bool    fDone;
        SkInterpolator fInterp;

        enum Direction
            {
            kUpDirection    = 1,
            kURDirection    = 1 << 1,
            kRightDirection = 1 << 2,
            kDRDirection    = 1 << 3,
            kDownDirection  = 1 << 4,
            kDLDirection    = 1 << 5,
            kLeftDirection  = 1 << 6,
            kULDirection    = 1 << 7
            };

        Direction fAnimationDirection;
        SkScalar fBegin[4];
        SkScalar fEnd[4];

        typedef SampleView INHERITED;
    };

SkView* create_transition(SkView* prev, SkView* next, int direction)
    {
    return SkNEW_ARGS(TransitionView, (prev, next, direction));
    };